package com.miaosha.miaosha.seckill;

import com.miaosha.miaosha.config.JedisPoolUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

@Component
public class SecKillRedis {
    private static int count = 0;

    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    public void setRedisTemplate(RedisTemplate<String,Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    private DefaultRedisScript<List> defaultRedisScript;

    @Autowired
    public void setDefaultRedisScript(DefaultRedisScript<List> defaultRedisScript) {
        this.defaultRedisScript = defaultRedisScript;
    }
    /**
     * lua实现秒杀
     * 解决库存遗留问题，同时也解决了库存超卖问题
     * @param userid
     * @param prodid
     * @return
     */
    /*public Integer doSecKill(String userid, String prodid) {
        //1 uid 和 proid非空判断
        if (null == userid || null == prodid) {
            System.out.println("系统错误");
            return 1;//系统错误
        }
        //设置lua脚本中的keys[1]和keys[2]变量，list是追加的看好变量的顺序
        List<String> keyList = new ArrayList();
        keyList.add(userid);
        keyList.add(prodid);
        //执行lua脚本
        List execute = redisTemplate.execute(defaultRedisScript, keyList);
        Long res = (Long) execute.get(0);
        if(res==0){
            System.out.println("没有库存,秒杀已结束");
            return 4;
        }else if(res==1){
            System.out.println("秒杀成功。你是第" + ++count+"个");
            return 0;
        }else if(res==2){
            System.out.println("已经秒杀过");
            return 3;
        }else{
            System.out.println("出现异常");
            return 1;
        }
    }*/
    /*static String secKillScript ="local userid=KEYS[1];\r\n" +
            "local prodid=KEYS[2];\r\n" +
            "local qtkey='sk:'..prodid..\":qt\";\r\n" +
            "local usersKey='sk:'..prodid..\":usr\";\r\n" +
            "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" +
            "if tonumber(userExists)==1 then \r\n" +
            "   return 2;\r\n" +
            "end\r\n" +
            "local num= redis.call(\"get\" ,qtkey);\r\n" +
            "if tonumber(num)<=0 then \r\n" +
            "   return 0;\r\n" +
            "else \r\n" +
            "   redis.call(\"decr\",qtkey);\r\n" +
            "   redis.call(\"sadd\",usersKey,userid);\r\n" +
            "end\r\n" +
            "return 1" ;*/

    private static String getScript() {
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new FileReader("src/main/resources/ms.lua"));
            String b;
            StringBuffer temp = new StringBuffer();
            while ((b = bufferedReader.readLine())!=null){
                temp.append(b+'\n');
            }
            return temp.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static int doSecKill(String userid, String prodid)  {
        Jedis jedis = JedisPoolUtils.getJedis();//获取redis连接

        String lua = jedis.scriptLoad(getScript());//添加到脚本缓存 并 生成 sha1 校验码
        Object result = jedis.evalsha(lua, 2, userid, prodid);//执行脚本
        String res = String.valueOf(result) ;
        //System.out.println(res);//调试
        if(res.equals("0")){
            System.out.println("没有库存,秒杀已结束");
            return 4;
        }else if(res.equals("1")){
            System.out.println("秒杀成功。你是第" + ++count+"个");
            return 0;
        }else if(res.equals("2")){
            System.out.println("已经秒杀过");
            return 3;
        }else{
            System.out.println("出现异常");
            return 1;
        }
    }


    /**
     * 秒杀
     * 开启事务解决库存超卖，但会产生库存遗留问题
     * @param userid 用户id
     * @param prodid 商品id
     * @return
     */
    public Integer doSecKill_back(String userid, String prodid) {
        //1 uid 和 proid非空判断
        if(null==userid || null==prodid){
            System.out.println("系统错误");
            return 1;//系统错误
        }
        //拼接相关的key
        //库存key
        //秒杀成功用户列表key
        //sk:0101:qt
        String kcKey = "sk:"+prodid+":qt";
        String userKey = "sk:"+prodid+":user";
        //秒杀过程
        SessionCallback<Object> callback = new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                //监视库存
                operations.watch(kcKey);
                //获取库存，如果库存为null，秒杀没有开始
                if(null==redisTemplate.opsForValue().get(kcKey)){
                    System.out.println("秒杀没有开始");
                    return 2;//秒杀没有开始
                }
                Integer kc = (Integer) redisTemplate.opsForValue().get(kcKey);
                //判断用户是否重复做秒杀操作
                //判断userid是否在清单集合中
                Boolean is = redisTemplate.opsForSet().isMember(userKey, userid);
                if(Boolean.TRUE.equals(is)){
                    System.out.println("已经秒杀成功，请勿重复秒杀");
                    return 3;//已经秒杀成功，请勿重复秒杀
                }
                //判断如果商品的数量，库存数量小于1，秒杀结束
                if(kc<1){
                    System.out.println("秒杀已结束");
                    return 4;//秒杀已结束
                }
                //使用事务
                operations.multi();
                //7.1 库存-1
                operations.opsForValue().decrement(kcKey);
                //7.2 秒杀成功的用户加入清单
                operations.opsForSet().add(userKey,userid);
                //提交事务
                List exec = operations.exec();
                if(exec.size()==0){
                    System.out.println("秒杀失败了");
                    return 5;
                }
                System.out.println("秒杀成功了");
                return 0;
            }
        };
        Object execute = redisTemplate.execute(callback);
        return (Integer) execute;
    }
}
